from eden.evo.aminos import UnaryAmino, BinaryAmino, EmptyAmino
from eden.evo.cell import NonsexualCell, SexualCell
from eden.evo.operands import ConstOperand, RegisterOperand
import doctest
import os

class Transcoder:
    INDENT = '    '
    
    def save_to_file(self, cells, dir, generation):
        filepath = os.path.join(dir, 'generation%04d.txt' % generation)
        file = open(filepath, 'w')
        for cell in cells:
            code = self.transcode_cell(cell)
            file.write(code)
            file.write('\n\n')
        file.close()
        
    def transcode_cell(self, cell):
        '''
        >>> from eden.evo.cell import *
        >>> from eden.evo.chromosome import *
        >>> from eden.evo.codon import *
        >>> from eden.evo.translate import *
        >>> chromosome = Chromosome((Codon(0, 128 + 2, 22), Codon(1, 128 + 3, 33)))
        >>> translator = Translator(Translator.get_predefined_encoding())
        >>> transcoder = Transcoder()
        
        >>> cell = NonsexualCell(chromosome, translator)
        >>> cell.id = 2
        >>> cell.generation = 1
        >>> cell.fitness = 0.573
        >>> cell.hitratio = 0.221
        >>> transcoder.transcode_cell(cell)
        'cell(id = 2, generation = 1) --> fitness = 0.573, hitratio = 0.221:\\n    chromosome:\\n        Null\\n        Add r3 33'
        
        >>> cell = SexualCell(chromosome, chromosome, translator)
        >>> cell.id = 2
        >>> cell.generation = 1
        >>> cell.fitness = 0.573
        >>> cell.hitratio = 0.221
        >>> transcoder.transcode_cell(cell)
        'cell(id = 2, generation = 1) --> fitness = 0.573, hitratio = 0.221:\\n    chromosome:\\n        Null\\n        Add r3 33\\n\\n    chromosome:\\n        Null\\n        Add r3 33'
        '''
        cell_line = 'cell(id = %d, generation = %d) --> fitness = %0.3f, hitratio = %0.3f:' % (cell.id, cell.generation, cell.fitness, cell.hitratio)
        if isinstance(cell, NonsexualCell):
            return '\n'.join([cell_line, Transcoder.INDENT + 'chromosome:'] + self.transcode_chromosome(cell.getchromosome(), cell.gettranslator()))
        elif isinstance(cell, SexualCell):
            code1 = '\n'.join([Transcoder.INDENT + 'chromosome:'] + self.transcode_chromosome(cell.getleft(), cell.gettranslator()))
            code2 = '\n'.join([Transcoder.INDENT + 'chromosome:'] + self.transcode_chromosome(cell.getright(), cell.gettranslator()))
            return cell_line + '\n' + code1 + '\n\n' + code2
        else:
            raise TypeError
    
    def transcode_chromosome(self, chromosome, translator):
        '''
        >>> from eden.evo.chromosome import *
        >>> from eden.evo.codon import *
        >>> from eden.evo.translate import *
        >>> chromosome = Chromosome((Codon(0, 128 + 1, 22), Codon(1, 128 + 2, 33)))
        >>> translator = Translator(Translator.get_predefined_encoding())
        >>> transcoder = Transcoder()
        >>> transcoder.transcode_chromosome(chromosome, translator)
        ['        Null', '        Add r2 33']
        '''
        protein = translator.translate(chromosome)
        lines = [self.transcode_amino(amino) for amino in protein]
        return lines
        
    def transcode_amino(self, amino):
        '''
        >>> from eden.evo.aminos import *
        >>> transcoder = Transcoder()
        >>> transcoder.transcode_amino(NullAmino(RegisterOperand(11), ConstOperand(22)))
        '        Null'
        >>> transcoder.transcode_amino(JumpAmino(RegisterOperand(11), ConstOperand(22)))
        '        Jump 22'
        >>> transcoder.transcode_amino(AddAmino(RegisterOperand(2), ConstOperand(22)))
        '        Add r2 22'
        '''
        operator = amino.__class__.__name__[:-5]
        loperand = self.transcode_operand(amino.getleft())
        roperand = self.transcode_operand(amino.getright())
        if isinstance(amino, EmptyAmino):
            return Transcoder.INDENT * 2 + operator
        elif isinstance(amino, UnaryAmino):
            return Transcoder.INDENT * 2 + '%s %s' % (operator, roperand)
        elif isinstance(amino, BinaryAmino):
            return Transcoder.INDENT * 2 + '%s %s %s' % (operator, loperand, roperand)
        
    def transcode_operand(self, operand):
        '''
        >>> transcoder = Transcoder()
        >>> transcoder.transcode_operand(ConstOperand(2))
        '2'
        >>> transcoder.transcode_operand(RegisterOperand(3))
        'r3'
        '''
        if isinstance(operand, ConstOperand):
            return str(operand.getvalue(None))
        elif isinstance(operand, RegisterOperand):
            return 'r' + str(operand.getindex())
        else:
            raise TypeError('Unknown operand type.')
    
if __name__ == '__main__':
    doctest.testmod()